#include <assert.h>
#include <driver.h>
#include <string.h>
#include <stdbool.h>
#include <mono/metadata/object.h>
#include <mono/metadata/exception.h>
#include <mono/metadata/appdomain.h>

extern void _start(void);

MonoMethod* lookup_interop_method(const char* method_name)
{
    MonoMethod* method = NULL;
    method = lookup_dotnet_method("PixiEditor.Extensions.Sdk.dll", "PixiEditor.Extensions.Sdk.Bridge", "Native", method_name, -1);
    assert(method);

    return method;
}

void invoke_interop_method(MonoMethod* method, void* params)
{
    MonoObject* exception = NULL;
    MonoObject* res = mono_runtime_invoke(method, NULL, params, &exception);
    if(exception != NULL)
    {
        mono_print_unhandled_exception(exception);
    }

    free(exception);
    free(method);
}

static const uint8_t key[16] = { };
static const uint8_t iv[16] = { };

__attribute__((import_name("get_encryption_key")))
const uint8_t* get_encryption_key() {
    return key;
}

__attribute__((import_name("get_encryption_iv")))
const uint8_t* get_encryption_iv() {
    return iv;
}

// Content is autogenerated
void attach_imported_functions(){}

void attach_internal_calls()
{
    attach_imported_functions();
    mono_add_internal_call("PixiEditor.Extensions.Sdk.Bridge.Native::get_encryption_key", get_encryption_key);
    mono_add_internal_call("PixiEditor.Extensions.Sdk.Bridge.Native::get_encryption_iv", get_encryption_iv);
}

void initialize_runtime(void)
{
    static int runtime_initialized = 0;

    if (runtime_initialized == 0) {
        _start();
        attach_internal_calls();
        runtime_initialized = 1;
    }
}

__attribute((export_name("load")))
void load()
{
    initialize_runtime();

    MonoMethod* metod = lookup_interop_method("Load");
    invoke_interop_method(metod, NULL);
}

__attribute((export_name("initialize")))
void initialize()
{
    MonoMethod* metod = lookup_interop_method("Initialize");
    invoke_interop_method(metod, NULL);
}

__attribute((export_name("get_encryption_key")))
const uint8_t* get_encryption_key_export()
{
    return get_encryption_key();
}

__attribute((export_name("get_encryption_iv")))
const uint8_t* get_encryption_iv_export()
{
    return get_encryption_iv();
}